# Clean workspace and load dependencies
rm(list = ls())
library(tidyr)
library(dplyr)
library(plotly)
library(ggplot2)
library(knitr)
library(lubridate)
# Function to load Xsense data (and convert time to seconds)
LoadXsenseData <- function(directory) {
files <- list.files(path = directory, full.names = TRUE) # Searches each marker file
data_list <- lapply(files, function(file) { # creates a list storing the data
data <- read.csv(file, header = TRUE, skip = 10) # reads each csv file
return(data)
})
names(data_list) <- paste0("data", seq_along(data_list)) # Names elements to my preference
# Re-align time
min_time <- min(sapply(data_list, function(df) min(df$SampleTimeFine))) # Identify the minimum SampleTimeFine value across all data
adjusted_data_list <- lapply(data_list, function(df) { # Subtract min_time from SampleTimeFine for each dataframe in data_list
df$SampleTimeFine <- df$SampleTimeFine - min_time
df$A_abs <- sqrt(df$FreeAcc_X^2 + df$FreeAcc_Y^2 + df$FreeAcc_Z^2)
df$TimeS <- (df$SampleTimeFine / 1e6) - (df$SampleTimeFine[1] / 1e6) # Converts time to seconds
return(df)
})
# Removing first elements of longer list, Find the minimum number of rows among all dataframes
min_rows <- min(sapply(adjusted_data_list, nrow))
# Remove elements from the beginning of each dataframe in adjusted_data_list
adjusted_data_list <- lapply(adjusted_data_list, function(df) {
if (nrow(df) > min_rows) {
df <- df[(nrow(df) - min_rows + 1):nrow(df), , drop = FALSE] # Remove elements from the beginning
}
return(df)
})
# Define Markers dataframe
markers <- data.frame(PacketCounter = adjusted_data_list[[1]]$PacketCounter,
SampleTimeFine = adjusted_data_list[[1]]$SampleTimeFine,
TimeS = adjusted_data_list[[1]]$TimeS)
# Add A_abs columns dynamically for each dataframe
for (i in 1:5) {
markers[paste0("A_abs", i)] <- adjusted_data_list[[i]]$A_abs
}
# Add FreeAcc_X, FreeAcc_Y, FreeAcc_Z columns for each dataframe
# for (i in 1:5) {
# markers[paste0("A_X", i)] <- adjusted_data_list[[i]]$FreeAcc_X
# markers[paste0("A_Y", i)] <- adjusted_data_list[[i]]$FreeAcc_Y
# markers[paste0("A_Z", i)] <- adjusted_data_list[[i]]$FreeAcc_Z
# }
return(markers) # Return the Markers dataframe
}
####################################################################
#### function to load all different measurements into "markers_list"
LoadMarkers <- function(folderdir) {
markers <- list() # Initialize an empty list to store the dataframes
folders <- list.dirs(folderdir, full.names = TRUE, recursive = FALSE)
for (i in seq_along(folders)) { # Iterate through each folder
data <- LoadXsenseData(folders[i]) # Load Xsense data from the current folder
markers[[paste0("markers", i)]] <- data # Store the dataframe in the list with a dynamic name
}
return(markers) # Return the list of dataframes
}
markers_list <- LoadMarkers("../../Logs") # Load all dataLoadXsense
This is my R code and output of the Xsense Dot IMU data. The source code can be found on my github
IMU’s do not start or stop measuring at the exact same moment in time. Thus, the amount of registration per IMU dffered (FreeAcc_X was of differing length). Assuming that the time in SampletimeFine is synchronized, I temporally realigned the data, excluding some of the first or last elements to ensure dataframes are of the same length
Code for loading the data
Normalise plots
Only the data during the lift should be considered. And I want to express time as percentage of the duration of an lift. Lets start by viewing the plot of the absolute acceleration of the barbell: Note that session 1 - 3 are test sessions.
The Y-axis scale differes. Note that for the strict press (session 4) the downward acceleration is almost similar to the upward acceleration. Thus, we are left with double the peaks in A_abs
Also, the difference between strict presses and push jerks becomes obvious when looking at upper leg acceleration:
Letst exlude the data before and after the push jerk. This is the data I want to clip:
cliptime <- data.frame(
session = c(NA, NA, NA, NA, 5, 6, 7),
start = c(NA, NA, NA, NA, 8.4, 10.81, 8),
end = c(NA, NA, NA, NA, 27.35, 33.3, 38)
)Code for clipping the data
# plot to visually inspect all the data, to determine where to clip. Would like to program this so it automatically identifies the start of movement patterns.
plot <- plot_ly(markers_list[[5]], x = ~TimeS, y = ~A_abs5, type = 'scatter', mode = 'lines', name = "Barbell") %>%
add_trace(y = ~A_abs3, name = "Bovenbeen") %>%
layout(xaxis = list(title = "TimeS"), yaxis = list(title = "A_abs"), title = "Strict Press")
# plot
rm(plot)clip_data <- function(cliptime, markers_list) {
sessions <- list()
for (i in seq_along(markers_list)) {
session_num <- i
if (!is.na(cliptime$session[session_num])) { # check if session should be clipped
filtered_df <- markers_list[[i]] %>%
filter(TimeS >= cliptime$start[session_num] & TimeS <= cliptime$end[session_num])
if (!is.null(filtered_df) && nrow(filtered_df) > 0) {
assign(paste0("session", session_num), filtered_df, envir = .GlobalEnv)
sessions[[paste0("session", session_num)]] <- filtered_df
}
}
}
return(sessions)
}Lets plot the clipped data.
Now lets focus on only one lift
# Create a list to store the plots
plots <- lapply(seq_along(sessions), function(i) {
# Plot Times against A_abs for the current dataframe
plot <- plot_ly(sessions[[i]], x = ~TimeS, y = ~A_abs5, type = 'scatter', mode = 'lines', name = paste("session", i+4)) %>%
add_trace(y = ~A_abs2, name = "Bovenbeen", line = list(width = 1)) %>%
layout(xaxis = list(title = "TimeS"), yaxis = list(title = "A_abs"))
return(plot)
})
# Combine plots into a single interactive plot
combined_plot <- subplot(plots, nrows = length(plots))
combined_plot